package common

import (
  "strings"
  "fmt"
  "gopkg.in/mgo.v2/bson"
  "time"
  "net/http"
  url2 "net/url"
  "encoding/json"
  "gopkg.in/mgo.v2"
  "sort"
  "io/ioutil"
  "errors"
)

const (
  topTmpCode             = "10731109444239"
  effectTemplateCode     = "10731062935624"
  omiCreateTopic         = "omi_create_topic"
  appKey                 = "6176113809265179692"
  hylandaUrl             = "http://api-v3.hylanda.com/api.php"
  testUrl                = "http://api.hylanda.com/api.explain.php"
  omiDeleteTopic         = "omi_delete_topic"
  omiModifyTopic         = "omi_modify_topic_status"
  omiModifyTemplateTopic = "omi_modify_template_topic_info"

)

var NewsBlackList = []string{
  "停股揭秘",
  "收盘：",
  "收评",
  "猜想及应对策略",
  "利好消息速递",
  "巨丰投顾",
  "的自选股",
  "重要提示：",
  "明日策略",
  "公司治理板块",
  "的独立意见",
  "研报汇编",
  "晨会纪要",
  "彬哥看盘",
  "博友早评",
  "盘中突破年线",
  "金百临",
  "秦洪",
  "曹先生",
  "快讯",
}

type Keyword struct {
  Force   []string `json:"Force"`
  Similar string `json:"Similar"`
}

type node struct {
  Label      string `json:"label"`
  NodeId     string `json:"nodeId"`
  MrwId      string `json:"mrwId"`
  Properties []*property `json:"properties"`
  Type       string `json:"type"`
}

type property struct {
  PropertyName string `json:"propertyName"`
  Value        string `json:"value"`
}

func getSign(formBody url2.Values) string {
  keys := []string{}
  for k, _ := range formBody {
    keys = append(keys, k)
  }
  sort.Strings(keys)
  join := ""
  for _, k := range keys {
    join += fmt.Sprintf("%s=%s&", k, formBody[k][0])
  }
  rs := []rune(join)
  join = string(rs[0:len(rs)-1])
  return HashMD5(join + "hylanda")
}

//构建强制词，近义词，排除词
func buildNodes(word *Keyword) map[string][]*node {
  force, similar := word.Force, word.Similar
  forceNodes := [] *node{
    {Label: "强制词1", NodeId: "80", MrwId: "546951"},
    {Label: "强制词2", NodeId: "98", MrwId: "546938"},
    {Label: "强制词3", NodeId: "92", MrwId: "546943"},
    {Label: "强制词4", NodeId: "86", MrwId: "546948"},
    {Label: "强制词5", NodeId: "74", MrwId: "546954"},
    {Label: "强制词6", NodeId: "68", MrwId: "546960"},
    {Label: "强制词7", NodeId: "62", MrwId: "546964"},
    {Label: "强制词8", NodeId: "104", MrwId: "546932"},
    {Label: "强制词9", NodeId: "470", MrwId: "546935"},
    {Label: "强制词10", NodeId: "465", MrwId: "547051"},
  }
  similarNode := &node{Label: "近义词", NodeId: "17", MrwId: "547167"}
  antonymNode := &node{Label: "排除词", NodeId: "11", MrwId: "547617"}
  nodes := []*node{}
  for i, node := range forceNodes {
    v := ""
    if len(force) > i {
      v = force[i]
    } else if len(force) > 0 {
      v = force[0]
    } else {
      v = similar
    }
    node.Properties = []*property{
      {PropertyName: "feawords", Value: v},
      {PropertyName: "ambiguwords", Value: ""},
    }
    node.Type = "FeatureWords"
    nodes = append(nodes, node)
  }
  similarNode.Properties = []*property{
    {PropertyName: "feawords", Value: similar},
    {PropertyName: "ambiguwords", Value: ""},
  }
  similarNode.Type = "FeatureWords"
  nodes = append(nodes, similarNode)
  antonymNode.Properties = []*property{
    {PropertyName: "feawords", Value: "请输入特征词"},
    {PropertyName: "ambiguwords", Value: ""},
  }
  antonymNode.Type = "FeatureWords"
  nodes = append(nodes, antonymNode)
  nodeMap := make(map[string][]*node)
  nodeMap["nodes"] = nodes
  return nodeMap
}

//只构建近义词
func buildTopInfoNodes(keyword *Keyword) map[string][]*node {
  similar := keyword.Similar
  similarNode := &node{Label: "近义词", NodeId: "17", MrwId: "547167"}
  similarNode.Properties = []*property{
    {PropertyName: "feawords", Value: similar},
    {PropertyName: "ambiguwords", Value: ""},
  }
  similarNode.Type = "FeatureWords"
  nodeMap := make(map[string][]*node)
  nodeMap["nodes"] = []*node{similarNode}
  return nodeMap
}

func createOneTopic(topic map[string]string, keyword *Keyword, effectTopicDao *mgo.Collection, code string) error {
  fmt.Printf("create topic name: %s\n", topic["topicName"])
  nodes := make(map[string][]*node)
  if topic["rtype"] == "theme" {
    nodes = buildNodes(keyword)
  } else if topic["rtype"] == "tmpInfo" {
    nodes = buildTopInfoNodes(keyword)
  } else if topic["rtype"] == "topInfo" {
    nodes = buildTopInfoNodes(keyword)
  }
  if len(nodes["nodes"]) == 0 {
    return NewError("nodes不能为空", DataError)
  }
  hylandaId, err := retryOmiCreateTopic(topic["topicName"], nodes, code)
  if err != nil {
    return err
  }
  now := time.Now()
  forces := keyword.Force
  force := ""
  if len(forces) > 0 {
    for _, v := range forces {
      force += v + ","
    }
    rs := []rune(force)
    force = string(rs[0:len(rs)-1])
  }
  return effectTopicDao.Insert(bson.M{
    "updatedAt": now,
    "createdAt": now,
    "rid":       topic["rid"],
    "topicName": topic["topicName"],
    "keyword": bson.M{
      "similar": keyword.Similar,
      "force":   force,
    },
    "hylandaId": hylandaId,
    "nextId":    InitNextId(),
    "__v":       0,
    "rtype":     topic["rtype"],
  })
}

func retryOmiCreateTopic(topicName string, nodes map[string][]*node, code string) (string, error) {
  count := 1
  var err error
  var hylandaId string = ""
  nodesStr, err := json.Marshal(nodes)
  if err != nil {
    return "", err
  }
  for ; count <= 5; count++ {
    if count > 1 {
      fmt.Printf("now retry create topicName: %s\n", topicName)
    }
    hylandaId, err = createTopic(topicName, string(nodesStr), code)
    if hylandaId != "" {
      return hylandaId, nil
    }
    time.Sleep(5 * time.Second)
  }
  return "", err
}

func createTopic(topicName string, nodes string, templateCode string) (string, error) {
  form := make(url2.Values)
  form["appKey"] = []string{appKey}
  form["op"] = []string{omiCreateTopic}
  form["topicName"] = []string{topicName}
  form["templateCode"] = []string{templateCode}
  form["nodes"] = []string{nodes}
  form["sign"] = []string{getSign(form)}
  form["nodes"] = []string{url2.QueryEscape(nodes)}
  form["topicName"] = []string{url2.QueryEscape(topicName)}
  //return "", NewError("testError", DataError)
  resBody, err := http.Post(hylandaUrl, "application/x-www-form-urlencoded", strings.NewReader(form.Encode()))
  defer resBody.Body.Close()
  if err != nil {
    return "", nil
  }
  reader, err := ioutil.ReadAll(resBody.Body)
  body := make(map[string]interface{})
  err = json.Unmarshal(reader, &body)
  if err != nil {
    return "", nil
  }
  if body["res"].(float64) != 0 {
    fmt.Println(body["msg"].(string))
    return "", NewError(body["msg"].(string), PostError)
  }
  return body["msg"].(string), nil
}

func CreateThemeTopic(name string, iCode string, qudao string, effectTopicDao *mgo.Collection) {
  name = strings.Replace(name, " ", "", -1)
  name = strings.Replace(name, "*", "", -1)
  name = strings.Replace(name, "-", "", -1)
  name = strings.Replace(name, "_", "", -1)
  topicMap := make(map[string]string)
  topicMap["rid"] = iCode
  topicMap["topicName"] = qudao + name
  topicMap["rtype"] = "theme"
  createOneTopic(topicMap, &Keyword{
    Force:   []string{name},
    Similar: name,
  }, effectTopicDao, effectTemplateCode)
}

func CreateTopInfoTopic(title string, id string, stocks []string, themes []string, rtype string, qudao string, effectTopicDao *mgo.Collection) error {
  title = strings.Replace(title, " ", "", -1)
  title = strings.Replace(title, "*", "", -1)
  title = strings.Replace(title, "-", "", -1)
  title = strings.Replace(title, "_", "", -1)
  title = strings.Replace(title, "「", "", -1)
  title = strings.Replace(title, "」", "", -1)
  title = strings.Replace(title, ".", "", -1)
  title = strings.Replace(title, "\"", "", -1)
  title = strings.Replace(title, "@", "", -1)
  title = strings.Replace(title, "#", "", -1)
  title = strings.Replace(title, "$", "", -1)
  title = strings.Replace(title, "\n", "", -1)
  values := MatchKeysFromString(NewsBlackList, title)
  if len(values) > 0 {
    return errors.New("标题包含黑名单关键字")
  }
  topicMap := map[string]string{
    "rid":       id,
    "topicName": title,
    "rtype":     rtype,
  }
  similar := ""
  for _, stock := range stocks {
    similar += stock + ","
  }
  for _, theme := range themes {
    similar += theme + ","
  }
  if similar == "" {
    return NewError("题材和个股都为空", DataError)
  }
  //fmt.Println(similar)
  //return nil
  rs := []rune(similar)
  similar = string(rs[0:len(rs)-1])
  return createOneTopic(topicMap, &Keyword{
    Similar: similar,
  }, effectTopicDao, topTmpCode)
}

func deleteTopic(hylandaId string) (string, error) {
  form := url2.Values{
    "appKey":    []string{appKey},
    "op":        []string{omiDeleteTopic},
    "topicCode": []string{hylandaId},
  }
  form["sign"] = []string{getSign(form)}
  resBody, err := http.Post(hylandaUrl, "application/x-www-form-urlencoded", strings.NewReader(form.Encode()))
  defer resBody.Body.Close()
  if err != nil {
    return "", err
  }
  reader, err := ioutil.ReadAll(resBody.Body)
  body := make(map[string]interface{})
  err = json.Unmarshal(reader, &body)
  if err != nil {
    return "", err
  }
  if body["res"].(float64) != 0 {
    return "", NewError(body["msg"].(string), PostError)
  }
  return body["msg"].(string), nil
}

func retryOmiDeleteTopic(hylandId string) (error) {
  count := 1
  var er error
  for ; count <= 5; count++ {
    if count > 1 {
      fmt.Printf("now retry delete topicCode: %s\n", hylandId)
    }
    _, err := deleteTopic(hylandId)
    if err == nil {
      return nil
    } else {
      er = err
    }
    time.Sleep(5 * time.Second)
  }
  return er
}

func DeleteOneTopic(hylandaId string, effectTopicDao *mgo.Collection) error {
  err := retryOmiDeleteTopic(hylandaId)
  if err != nil {
    return err
  }
  return effectTopicDao.Update(bson.M{
    "hylandaId": hylandaId,
  }, bson.M{
    "$set": bson.M{
      "disabled": true,
    },
  })
}

func CloseOneTopic(hylandaId string) error {
  form := url2.Values{
    "appKey":     []string{appKey},
    "op":         []string{omiModifyTopic},
    "topicCodes": []string{hylandaId},
    "status":     []string{"0"},
  }
  form["sign"] = []string{getSign(form)}
  resBody, err := http.Post(hylandaUrl, "application/x-www-form-urlencoded", strings.NewReader(form.Encode()))
  defer resBody.Body.Close()
  if err != nil {
    return err
  }
  reader, err := ioutil.ReadAll(resBody.Body)
  body := make(map[string]interface{})
  err = json.Unmarshal(reader, &body)
  if err != nil {
    return nil
  }
  if body["res"].(float64) != 0 {
    return NewError(body["msg"].(string), PostError)
  }
  return nil
}

func UpdateOneTopic(hylandaId string, stocks [] string, themes [] string) error {
  similar := ""
  for _, stock := range stocks {
    similar += stock + ","
  }
  for _, theme := range themes {
    similar += theme + ","
  }
  if similar == "" {
    return NewError("题材和个股都为空", DataError)
  }
  rs := []rune(similar)
  similar = string(rs[0:len(rs)-1])
  fmt.Printf("update topic id: %s\n", hylandaId)
  nodes := buildUpdateNodes(similar)
  if len(nodes["nodes"]) == 0 {
    return NewError("nodes不能为空", DataError)
  }
  nodesStr, err := json.Marshal(nodes)
  if err != nil {
    return err
  }
  form := url2.Values{
    "appKey":       []string{appKey},
    "op":           []string{omiModifyTemplateTopic},
    "templateCode": []string{topTmpCode},
    "topicCode":    []string{hylandaId},
    "nodes":        []string{string(nodesStr)},
  }
  form["sign"] = []string{getSign(form)}
  form["nodes"] = []string{url2.QueryEscape(string(nodesStr))}
  resBody, err := http.Post(hylandaUrl, "application/x-www-form-urlencoded", strings.NewReader(form.Encode()))
  defer resBody.Body.Close()
  if err != nil {
    return err
  }
  reader, err := ioutil.ReadAll(resBody.Body)
  body := make(map[string]interface{})
  err = json.Unmarshal(reader, &body)
  if err != nil {
    return err
  }
  if body["res"].(float64) != 0 {
    return NewError(body["msg"].(string), PostError)
  }
  fmt.Println(body["msg"].(string))
  return nil
}

func GetTemplate(topicCode string) error {
  form := url2.Values{
    "appKey":    []string{appKey},
    "op":        []string{"omi_fetch_template_topic_info"},
    "topicCode": []string{topicCode},
  }
  form["sign"] = []string{getSign(form)}
  fmt.Println(form["sign"])
  resBody, err := http.Post(hylandaUrl, "application/x-www-form-urlencoded", strings.NewReader(form.Encode()))
  if err != nil {
    return err
  }
  reader, err := ioutil.ReadAll(resBody.Body)
  body := make(map[string]interface{})
  err = json.Unmarshal(reader, &body)
  if err != nil {
    return err
  }
  if body["res"].(float64) != 0 {
    return NewError(body["msg"].(string), PostError)
  }
  fmt.Println(body["msg"].(string))
  return nil
}

type updateProperty struct {
  PropertyName string `json:"propertyName"`
  Value        string `json:"value"`
  ViewName     string `json:"viewName"`
}

func buildUpdateNodes(similar string) map[string][]map[string]interface{} {
  nodes := make([]map[string]interface{}, 5)
  nodes[0] = map[string]interface{}{
    "createMergeTag":    true,
    "definedNodesOrder": -1,
    "iconPath":          "http://decisiondiagram3x.hylanda.com/DecisionDiagService/decision/stencils/clipart/FeatureWords_defTaskNode_reference.png",
    "label":             "近义词",
    "mrwId":             "547167",
    "nodeId":            "1289",
    "nodeState":         0,
    "properties": []*updateProperty{
      &updateProperty{
        PropertyName: "feawords",
        Value:        similar,
        ViewName:     "特征词",
      },
      &updateProperty{
        PropertyName: "ambiguwords",
        Value:        "",
        ViewName:     "歧义词",
      },
    },
    "tag":  false,
    "type": "FeatureWords",
  }
  nodes[1] = map[string]interface{}{
    "createMergeTag":    false,
    "definedNodesOrder": -1,
    "iconPath":          "http://decisiondiagram3x.hylanda.com/DecisionDiagService/decision/stencils/clipart/NewFeatureWords_defTaskNode.png",
    "label":             "url屏蔽-自定义",
    "lastModifiedTime":  "",
    "mrwId":             "",
    "nodeId":            "988",
    "nodeState":         1,
    "properties": []*updateProperty{
      &updateProperty{
        PropertyName: "feawords",
        Value:        "请输入特征词",
        ViewName:     "特征词",
      },
      &updateProperty{
        PropertyName: "ambiguwords",
        Value:        "请输入歧义词",
        ViewName:     "歧义词",
      },
      &updateProperty{
        PropertyName: "tag",
        Value:        "否",
        ViewName:     "标签",
      },
    },
    "tag":  false,
    "type": "NewFeatureWords",
  }
  nodes[2] = map[string]interface{}{
    "createMergeTag":    false,
    "definedNodesOrder": -1,
    "iconPath":          "http://decisiondiagram3x.hylanda.com/DecisionDiagService/decision/stencils/clipart/NewFeatureWords_defTaskNode.png",
    "label":             "标题排除词-自定义",
    "lastModifiedTime":  "",
    "mrwId":             "",
    "nodeId":            "990",
    "nodeState":         1,
    "properties": []*updateProperty{
      &updateProperty{
        PropertyName: "feawords",
        Value:        "请输入特征词",
        ViewName:     "特征词",
      },
      &updateProperty{
        PropertyName: "ambiguwords",
        Value:        "请输入歧义词",
        ViewName:     "歧义词",
      },
      &updateProperty{
        PropertyName: "tag",
        Value:        "否",
        ViewName:     "标签",
      },
    },
    "tag":  false,
    "type": "NewFeatureWords",
  }
  nodes[3] = map[string]interface{}{
    "createMergeTag":    false,
    "definedNodesOrder": -1,
    "iconPath":          "http://decisiondiagram3x.hylanda.com/DecisionDiagService/decision/stencils/clipart/NewFeatureWords_defTaskNode.png",
    "label":             "正文排除词--自定义",
    "lastModifiedTime":  "",
    "mrwId":             "",
    "nodeId":            "982",
    "nodeState":         1,
    "properties": []*updateProperty{
      &updateProperty{
        PropertyName: "feawords",
        Value:        "请输入特征词",
        ViewName:     "特征词",
      },
      &updateProperty{
        PropertyName: "ambiguwords",
        Value:        "请输入歧义词",
        ViewName:     "歧义词",
      },
      &updateProperty{
        PropertyName: "tag",
        Value:        "否",
        ViewName:     "标签",
      },
    },
    "tag":  false,
    "type": "NewFeatureWords",
  }
  nodes[4] = map[string]interface{}{
    "createMergeTag":    false,
    "definedNodesOrder": -1,
    "iconPath":          "http://decisiondiagram3x.hylanda.com/DecisionDiagService/decision/stencils/clipart/FeatureWords_defTaskNode_reference.png",
    "label":             "出处排除词--自定义",
    "lastModifiedTime":  "2016-01-05 11:39:29 | 059",
    "mrwId":             "547617",
    "nodeId":            "14",
    "nodeState":         1,
    "properties": []*updateProperty{
      &updateProperty{
        PropertyName: "feawords",
        Value:        "请输入特征词",
        ViewName:     "特征词",
      },
      &updateProperty{
        PropertyName: "ambiguwords",
        Value:        "请输入歧义词",
        ViewName:     "歧义词",
      },
      &updateProperty{
        PropertyName: "tag",
        Value:        "否",
        ViewName:     "标签",
      },
    },
    "tag":  false,
    "type": "FeatureWords",
  }
  return map[string][]map[string]interface{}{
    "nodes": nodes,
  }
}
